/**
 * @file algo_Filter.c
 * @brief Software implementation of crc for 51 microcontroller.
 * @author David Xu & J. Camilo Gomez C.
 * @version 1.0.0
 * @date 2023-11-21
 * @note none
 */

#include "algo_crc.h"

/*============================================================================*/
// the CRC8 calculation for computime the checksum
// polynomial: x^8 + x^5 + x^4 + 1

#define CRC8_INIT       0xff    // initial value
#define CRC8_POLYNOMIAL 0x31    // this is CRC-8/Maxim algorithm

uint8_t sensirion_common_generate_crc(uint8_t *data, uint16_t count) {
    uint16_t current_byte;
    uint8_t  crc = CRC8_INIT;
    uint8_t  crc_bit;
    // calculates 8-Bit checksum with given polynomial
    for (current_byte = 0; current_byte < count; ++current_byte) {
        crc ^= (data[current_byte]);
        for (crc_bit = 8; crc_bit > 0; --crc_bit) {
            if (crc & 0x80)
                crc = (crc << 1) ^ CRC8_POLYNOMIAL;
            else
                crc = (crc << 1);
        }
    }
    return crc;
}

/*============================================================================*/
static uint32_t qCRCx_Reflect(uint32_t xData, const uint8_t nBits) {
    uint32_t r = 0;
    uint8_t  xBit;
    /*Reflect the data about the center bit*/
    for (xBit = 0u; xBit < nBits; ++xBit) {
        /*if the LSB bit is set, set the reflection of it*/
        if (0u != (xData & 0x01u)) {
            r |= (uint32_t)(1u << ((nBits - 1u) - xBit));
        }
        xData >>= 1u;
    }

    return r;
}
/*============================================================================*/
// the CRC8/CRC16/CRC32 calculation for computime the checksum
uint32_t qCRCx(const qCRC_Mode_t mode, const void *const pData, const size_t length,
               uint32_t poly, const uint32_t init, const uint8_t refIn,
               const uint8_t refOut, uint32_t xorOut) {
    uint32_t crc = 0u;
    if ((NULL != pData) && (length > 0u) && (mode >= QCRC8) && (mode <= QCRC32)) {
        size_t               i;
        uint8_t              xBit;
        const uint32_t       widthValues[3] = { 8uL, 16uL, 32uL };
        const uint32_t       width          = widthValues[mode];
        uint8_t const *const msg            = pData;
        const uint32_t       wd1            = (uint32_t)width - 8u;
        const uint32_t       topBit         = (uint32_t)1u << (width - 1u);
        const uint32_t       bitMask        = (0xFFFFFFFFu >> (32u - width));
        poly &= bitMask;
        xorOut &= bitMask;
        crc = init;
        /*Perform modulo-2 division, a byte at a time. */
        for (i = 0u; i < length; ++i) {
            crc ^= (0u != refIn) ? (qCRCx_Reflect((uint32_t)msg[i], 8u) << wd1)
                                 : ((uint32_t)msg[i] << wd1);
            for (xBit = 8u; xBit > 0u; --xBit) {
                /*try to divide the current data bit*/
                crc = (0u != (crc & topBit)) ? ((crc << 1u) ^ poly) : (crc << 1u);
            }
        }
        crc = (0u != refOut) ? (qCRCx_Reflect(crc, (uint8_t)width) ^ xorOut)
                             : (crc ^ xorOut);
        crc &= bitMask;
    }

    return crc;
}
/*============================================================================*/
